/*
* Conditions Of Use
*
* This software was developed by employees of the Sigmatrix(Beijing) Corporation.
* This software is provided by sigmatrix as a service and is expressly
* provided "AS IS."  Sigmatrix MAKES NO WARRANTY OF ANY KIND, EXPRESS, IMPLIED
* OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT
* AND DATA ACCURACY.  Sigmatrix does not warrant or make any representations
* regarding the use of the software or the results thereof, including but
* not limited to the correctness, accuracy, reliability or usefulness of
* the software.
*
* Permission to use this software is contingent upon your acceptance
* of the terms of this agreement.
*
*/
package com.sigmatrix.h5.service.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.alibaba.fastjson.JSONObject;
import com.sigmatrix.h5.dao.DismantlePileStackDao;
import com.sigmatrix.h5.dto.BaseQRCodeData;
import com.sigmatrix.h5.dto.DismantlePileStackData;
import com.sigmatrix.h5.dto.ProductData;
import com.sigmatrix.h5.dto.QRCodeError;
import com.sigmatrix.h5.entity.DismantlePileStack;
import com.sigmatrix.h5.entity.MultistageLevelQRCode;
import com.sigmatrix.h5.enums.PackScanOperTypeEnum;
import com.sigmatrix.h5.enums.ResponseEnum;
import com.sigmatrix.h5.exception.DaoException;
import com.sigmatrix.h5.exception.ServiceException;
import com.sigmatrix.h5.response.Response;
import com.sigmatrix.h5.service.DismantlePileStackService;
import com.sigmatrix.h5.service.MultistageLevelQrCodeService;
import com.sigmatrix.h5.service.ProductService;
import com.sigmatrix.h5.service.QrCodeService;
import com.sigmatrix.h5.service.common.impl.BaseServiceImpl;
import com.sigmatrix.h5.utils.Cons;
import com.sigmatrix.h5.web.vo.PackBoxStackVo;
import com.sigmatrix.h5.web.vo.QRCodeVo;

/**
 *@ClassName: DismantlePileStackServiceImpl
 *@Description: 拆组垛业务层接口
 *@author youjun
 *@date 2017-08-17 20:37:22
 */
@Service(value="dismantlePileStackService")
public class DismantlePileStackServiceImpl extends BaseServiceImpl<DismantlePileStackData> implements DismantlePileStackService {

	/**logger record*/
	private static final Logger LOGGER = LoggerFactory.getLogger(DismantlePileStackServiceImpl.class);
	
	/**拆组垛数据库操作接口*/
	@Autowired
	private transient DismantlePileStackDao dismantlePileStackDao;
	
	/**多级码业务层接口*/
	@Autowired
	private transient MultistageLevelQrCodeService multistageLevelQRCodeService;
	
	/**扫码业务层接口*/
	@Autowired
	private transient QrCodeService qRCodeService;
	
	/**产品业务层接口*/
	@Autowired
	private transient ProductService productService;

	/**(non-Javadoc)
	 *@Title: upload
	 *@Description: 
	 *@see com.sigmatrix.sc.handheld.inter.service.DismantlePileStackService#upload(com.sigmatrix.sc.handheld.inter.web.vo.packmanage.PackBoxStackVo)
	 *@author youjun
	 *@date 2017年8月18日 上午2:58:01
	 */
	@SuppressWarnings("unchecked")
	@Transactional(rollbackFor=ServiceException.class)
	@Override
	public Response upload(final PackBoxStackVo packBoxStackVo) throws ServiceException {

		LOGGER.info("拆组垛操作传递参数为:{}", JSONObject.toJSONString(packBoxStackVo));
		// 参数验证  。。。。。。。
		Response validateResponse = validateUploadParams(packBoxStackVo);
		if(validateResponse.getCode()!=Cons.RESPONSE_CODE_SUCCESS) {
			return validateResponse;
		}
		try{
			final String enterpriseCode = packBoxStackVo.getEnterpriseCode();
			final String workOrder = packBoxStackVo.getWorkOrder();
			// 验证工单号是否已经存在
			if(validateIsExist(workOrder, enterpriseCode)){
				return new Response(Cons.RESPONSE_CODE_EXISTED_DATA, "工单号已存在");
			}
			
			final int operationType = packBoxStackVo.getOperationType(); // 操作方式:拆垛、补垛、组新垛
			final String targetCode = packBoxStackVo.getCode(); // 操作的目标对象码,拆垛时为垛码(最高规格码),补垛时为上级码,组新垛时也为垛码
			List<String> tragetCodeHaveChildPackList = new ArrayList<String>(); // 目标码拥有的包装码集合
			final String operateCodes = packBoxStackVo.getOperateCodes(); // 操作的目标对象码下的包装码
			final String[] operateCodeArray = operateCodes.split(",");
			final List<String> operateCodeList = new ArrayList<String>();
			CollectionUtils.addAll(operateCodeList, operateCodeArray);
			final int productId = packBoxStackVo.getProductId(); // 当前操作的产品ID
			final int warehouseId = packBoxStackVo.getWarehouseId(); // 当前操作的仓库
			final Map<String, Object> map = new HashMap<String, Object>();
			final QRCodeVo qRCodeVo = new QRCodeVo();
			qRCodeVo.setCode(targetCode);
			qRCodeVo.setCodeType(Cons.CODE_TYPE_CIPHER);
			qRCodeVo.setEnterpriseCode(enterpriseCode);
			// 查询操作的目标码是否符合要求
			validateResponse = qRCodeService.queryCodeByCode(qRCodeVo, map, -1);
			if(validateResponse.getCode()!=Cons.RESPONSE_CODE_SUCCESS) {
				return validateResponse;
			}
			final ProductData product = (ProductData) map.get("product");
			final int packLevel = product.getProductUnit().intValue(); // 包装单位级别
			final String packUnit = product.getPackUnit(); // 包装单位名称
			final int codeLevel = (int) map.get("codeLevel"); // 扫码级别
			product.getPackStandardLevelList();
			final int bigLevel = product.getBigLevel();
			final Map<Integer, String> packStandardMap = product.getPackStandardMap();
			int subNum = 0; // 目标码对应的规格拥有的箱码数量
			int childPackNum = 0; // 目标码现有的包装码数量
			final MultistageLevelQRCode targetMultis = (MultistageLevelQRCode) map.get("multistageLevelQRCode"); // 目标码
			/*
			 *  1、验证操作对象码：
			 *  1)、与传递的参数productId同产品
			 *  2)、拆垛、组新垛时必须为垛码，补垛时必须为上级码，
			 *  3)、验证能操作码的个数：
			 *  	补垛:上级码剩余空位>=操作的箱码数;
			 *  	拆垛:当前垛具有的箱码数量>=操作的包装码数;
			 *  	组新垛:最大包装规格(垛)拥有的包装单位数量>=操作的包装码(箱)数
			 */
			String operationTypeDesc = "补";
			if(product.getProductId()!=productId) {
				return new Response(Cons.RESPONSE_CODE_PARAM_INVALID, new StringBuilder("传递的产品和操作的目标")
						.append(packStandardMap.get(codeLevel)).append("所属产品不一致").toString());
			}
			if(operationType==PackScanOperTypeEnum.GROUP_OR_FILL_STACK.getId()) { // 补垛,目标码为包装码的上级码
				if(codeLevel-1!=packLevel) {
					return new Response(Cons.RESPONSE_CODE_EXCEPTION, new StringBuilder("补").append(packStandardMap.get(bigLevel))
							.append("操作目标码应为:").append(packStandardMap.get(packLevel+1))
							.append(",当前为:").append(packStandardMap.get(codeLevel)).toString());
				}
				subNum = productService.getCountHaveSpecifiedLevelCount(product, codeLevel, packLevel); // 包装的上级码具有的包装码数量
				childPackNum = targetMultis.getChildNum();
				final String[] childs = targetMultis.getChilds().split(",");
				CollectionUtils.addAll(tragetCodeHaveChildPackList, childs);
			}else { // 拆垛、组新垛,目标码为最大级别码
				operationTypeDesc = operationType==PackScanOperTypeEnum.SPLIT_STACK.getId()?"拆":"组";
				if(codeLevel!=bigLevel) {
					return new Response(Cons.RESPONSE_CODE_EXCEPTION, new StringBuilder(operationTypeDesc).append(packStandardMap.get(bigLevel))
							.append("操作目标码应为:").append(packStandardMap.get(bigLevel))
							.append(",当前为:").append(packStandardMap.get(codeLevel)).toString());
				}
				subNum = productService.getCountHaveSpecifiedLevelCount(product, bigLevel, packLevel); // 最大级别码具有的包装码数量
				final Map<String, Object> resultMap = multistageLevelQRCodeService.getInventedCodeRelatedInfo(enterpriseCode, 
						targetCode, codeLevel, false, packLevel, false);
				childPackNum = (int) resultMap.get("packCodeCount"); // 目标码现有包装码数量
				tragetCodeHaveChildPackList = (List<String>) resultMap.get("packCodeList");
			}
			if(operationType==PackScanOperTypeEnum.GROUP_OR_FILL_STACK.getId()) { // 补垛:目标码（包装码的上级码）剩余空位>=操作的包装数
				if(subNum-childPackNum<operateCodeArray.length){
					return new Response(Cons.RESPONSE_CODE_PARAM_INVALID, new StringBuilder("当前补")
							.append(packStandardMap.get(bigLevel)).append("操作当前").append(packStandardMap.get(codeLevel))
							.append("只允许补")
							.append(subNum-childPackNum).append(packStandardMap.get(packLevel)).toString());
				}
			}else if(operationType==PackScanOperTypeEnum.SPLIT_STACK.getId()) { // 拆垛:当前垛具有的包装码数量>=操作的包装码数
				if(childPackNum<operateCodeArray.length) {
					return new Response(Cons.RESPONSE_CODE_PARAM_INVALID, new StringBuilder("当前")
							.append(operationTypeDesc)
							.append(packStandardMap.get(bigLevel)).append("操作最多拆").append(childPackNum)
							.append(packStandardMap.get(packLevel-1)).toString());
				}
			}else{ // 组新垛:初始具有的箱数应为0,subNum>=操作的包装码数
				if(childPackNum!=0) { // 组新垛,初始的包含包装码数量必须为0
					return new Response(Cons.RESPONSE_CODE_DATA_EXCEPTION, new StringBuilder("组新")
							.append(packStandardMap.get(bigLevel))
							.append("操作").append(packStandardMap.get(bigLevel)).append("码不允许拥有")
							.append(packUnit).toString());
				}else if(subNum<operateCodeArray.length){
					return new Response(Cons.RESPONSE_CODE_DATA_EXCEPTION, new StringBuilder("组新")
							.append(packStandardMap.get(bigLevel))
							.append("操作").append(packStandardMap.get(bigLevel)).append("最多组").append(subNum)
							.append(packUnit).toString());
				}
			}
			// 验证组/拆箱的一系列码
			/*
			 * 2、验证所操作的包装码对应的下级码如下几点:
			 * 1)、是否为指定产品(同产品)
			 * 2)、codeLevel是否正确:即是否为包装码的下级码
			 * 3)、组/补箱的码必须是无上级码的(无上级关联关系)，拆箱的码必须是属于当前的箱码的(上级码是当前箱码)
			 */
			final List<QRCodeError> qRCodeErrorList = new ArrayList<QRCodeError>();
			validateOperateCode(enterpriseCode, qRCodeErrorList, operateCodeArray, targetCode, tragetCodeHaveChildPackList, product, operationType);
			if(!CollectionUtils.isEmpty(qRCodeErrorList)) {
				// 排除重复验证结果
				final List<QRCodeError> errorList = QRCodeError.getNotRepetition(qRCodeErrorList);
				final Map<String, Object> errorMap = new HashMap<String, Object>();
				errorMap.put("failNum", errorList.size()); // 失败总数
				errorMap.put("codeInfo", errorList); // 失败列表
				final Response errorResponse = new Response(Cons.RESPONSE_CODE_PACK_BOX_STOCK_EXCEPTION, "码信息有误", errorMap);
				LOGGER.info("码信息有误:{}", JSONObject.toJSONString(errorResponse));
				return errorResponse;
			}
			// 记录拆/补垛操作
			final DismantlePileStack dismantlePileStack = new DismantlePileStack();
			dismantlePileStack.setCreateTime(new Date());
			String bigCode = targetCode;
			if(operationType==PackScanOperTypeEnum.GROUP_OR_FILL_STACK.getId()) { // 补垛目标码为上级码,需拿到最大码
				if(bigLevel-1!=packLevel) {
					final MultistageLevelQRCode bigMulti = multistageLevelQRCodeService.getSpecifiedUpLevelCode(enterpriseCode, targetCode, packLevel+1, false, bigLevel);
					bigCode = bigMulti!=null?bigMulti.getPackContent():"";
					LOGGER.error("补垛操作传递的层无法获得上级最大规格码");
				}
			}
			dismantlePileStack.setCurrentWrapCode(bigCode); // 操作的最大级别码
			dismantlePileStack.setSubWrapCodes(operateCodes); // 操作的包装码
			dismantlePileStack.setNumber(operateCodeArray.length);
			Short opreType = Cons.PACK_STACK_FILL; // 组新垛/补垛
			if(operationType==PackScanOperTypeEnum.SPLIT_STACK.getId()) { // 拆垛
				opreType = Cons.PACK_STACK_SPLIT;
			}
			dismantlePileStack.setOpreType(opreType);
			dismantlePileStack.setProductId(productId);
			dismantlePileStack.setTerminalId(packBoxStackVo.getTerminalId());
//			dismantlePileStack.setTerminalOpreId(packBoxStackVo.getUserId());
			dismantlePileStack.setWarehouseId(warehouseId);
			dismantlePileStack.setWorkOrder(workOrder);
			dismantlePileStackDao.save(dismantlePileStack , enterpriseCode);
			// 拆、组新/补垛关系
			multistageLevelQRCodeService.packGroupSplitStack(enterpriseCode, targetCode, operateCodeList, operationType, product);
			final Map<String, Object> resultMap = getPackGroupSplitStackResult(enterpriseCode, bigCode , product);
			return new Response(ResponseEnum.SUCCESS, resultMap);
		}catch(Exception e) {
			LOGGER.error("upload fail", e);
			throw new ServiceException(e);
		}
	}

	/**
	 *@Title: getPackGroupSplitStackResult
	 *@Description: TODO()
	 *@return Map<String,Object>
	 *@author youjun
	 *@date 2017年8月21日 下午2:36:44
	 *@param enterpriseCode
	 *@param bigCode
	 *@param product
	 *@return
	 */
	private Map<String, Object> getPackGroupSplitStackResult(final String enterpriseCode, final String bigCode,
			final ProductData product) throws ServiceException {

		if(StringUtils.isBlank(bigCode)
				||StringUtils.isBlank(enterpriseCode)
				||product==null) {
			throw new ServiceException("获得拆组最大单位失败");
		}
		final Map<String, Object> map = new HashMap<String, Object>();
		final Map<String, Object> resultMap = multistageLevelQRCodeService.getInventedCodeRelatedInfo(enterpriseCode, bigCode, product.getBigLevel(), false, 
				product.getProductUnit().intValue(), true);
		if(resultMap.get("multistageLevelQRCode")==null) {
			throw new ServiceException("最大码不存在");
		}
		final List<BaseQRCodeData> codeList = new ArrayList<BaseQRCodeData>();
		map.put("count", (int)resultMap.get("packCodeCount"));
		final Map<Integer, String> packStandardMap = product.getPackStandardMap();
		@SuppressWarnings("unchecked")
		final List<MultistageLevelQRCode> childs = (List<MultistageLevelQRCode>) resultMap.get("packMultistageLevelList");
		if(!CollectionUtils.isEmpty(childs)) {
			BaseQRCodeData baseQRCodeData = null;
			for(final MultistageLevelQRCode child:childs) {
				baseQRCodeData = new BaseQRCodeData();
				baseQRCodeData.setCode(child.getPackContent());
				baseQRCodeData.setFriendlyCode(child.getFriendlyCode());
				baseQRCodeData.setFriendlyCodeFront(child.getFriendlyCodeFront());
				baseQRCodeData.setFriendlyCodeEnd(child.getFriendlyCodeEnd());
				baseQRCodeData.setSeqNum(child.getSeqNum());
				baseQRCodeData.setBigUnit(packStandardMap.get(product.getBigLevel()));
				baseQRCodeData.setPackUnit(product.getPackUnit());
				baseQRCodeData.setPackUnderUnit(packStandardMap.get(product.getProductUnit()-1));
				codeList.add(baseQRCodeData);
			}
		}
		map.put("codeList", codeList);
		return map;
	}

	/**
	 *@Title: validateOperateCode
	 *@Description: 验证所操作的最大级别码对应的包装码如下几点:<br/>
			 * 1)、是否为指定产品(同产品)<br/>
			 * 2)、codeLevel是否正确:即是否为包装码<br/>
			 * 3)、组新垛、补垛的码必须是无上级码的(无上级关联关系)，拆垛的码必须是属于当前的垛码的<br/>
	 *@return void
	 *@author youjun
	 *@date 2017年8月19日 下午9:08:43
	 *@param enterpriseCode
	 *@param qRCodeErrorList
	 *@param operateCodeArray
	 *@param targetCode 目标码
	 *@param tragetCodeHaveChildPackList 目标码拥有的包装码集合
	 *@param product
	 *@param operationType
	 * @throws ServiceException 
	 */
	private void validateOperateCode(String enterpriseCode, List<QRCodeError> qRCodeErrorList,
			String[] operateCodeArray, String targetCode, final List<String> tragetCodeHaveChildPackList, ProductData product, int operationType) throws ServiceException {

		if(StringUtils.isBlank(enterpriseCode)
				||qRCodeErrorList==null
				||operateCodeArray==null
				||tragetCodeHaveChildPackList==null) {
			throw new ServiceException("验证所操作的包装码对应的下级码参数错误");
		}
		final int productId = product.getProductId();
		final int packLevel = product.getProductUnit().intValue();
		final String packUnit = product.getPackUnit(); // 包装单位
		final Map<Integer, String> packStandardMap = product.getPackStandardMap();
		final int bigLevel = product.getBigLevel(); // 最大包装规格
		final String bigUnit = packStandardMap.get(bigLevel); // 最大包装规格名称
		QRCodeVo qRCodeVo = null;
		QRCodeError qRCodeError = null;
		StringBuilder errorMsg = null;
		for(final String operateCode:operateCodeArray) {
			final Map<String, Object> map = new HashMap<String, Object>();
			qRCodeVo = new QRCodeVo(enterpriseCode, operateCode, Cons.CODE_TYPE_CIPHER);
			final Response response = qRCodeService.queryCodeByCode(qRCodeVo, map, -1);
			if(response.getCode()!=Cons.RESPONSE_CODE_SUCCESS) {
				qRCodeError = new QRCodeError(operateCode, null, productId, response.getMessage());
			}else{
				final String parentCode = (String) map.get("parentCode"); // 上级码
				final int codeLevel = (int) map.get("codeLevel");
				final int codeProductId = product.getProductId();
				// 是否为指定产品(同产品)
				if(productId!=product.getProductId()) {
					errorMsg = new StringBuilder("不属于当前产品");
					qRCodeError = new QRCodeError(operateCode, codeLevel, codeProductId, errorMsg.toString());
				}else if(codeLevel!=packLevel){ // codeLevel是否正确
					errorMsg = new StringBuilder("当前为").append(packStandardMap.get(codeLevel));
					errorMsg.append("，请扫").append(packUnit);
					qRCodeError = new QRCodeError(operateCode, codeLevel, codeProductId, errorMsg.toString());
				}else{ // 组新/补垛的码必须是无上级码的(无上级关联关系)且是满包装的，拆垛的码必须是属于当前的垛码的(顶级码是当前垛码)
					if((operationType==PackScanOperTypeEnum.GROUP_OR_FILL_STACK.getId()
							||operationType==PackScanOperTypeEnum.ADD_NEW_STACK.getId())) { // 组新/补垛
						if(StringUtils.isNotBlank(parentCode)) {
							errorMsg = new StringBuilder("当前").append(packStandardMap.get(codeLevel));
							errorMsg.append("存在").append(packStandardMap.get(codeLevel));
							errorMsg.append(packStandardMap.get(codeLevel+1)).append("关联");
							qRCodeError = new QRCodeError(operateCode, codeLevel, codeProductId, errorMsg.toString());
						}else { // 仅组新/补垛操作(拆垛操作不判断)时判断操作码是否满包装
							final MultistageLevelQRCode multis = (MultistageLevelQRCode) map.get("multistageLevelQRCode");
							qRCodeError =  multistageLevelQRCodeService.validateCodeIsFullPack(multis, product);
						}
					}else if(operationType==PackScanOperTypeEnum.SPLIT_STACK.getId()
							&&!tragetCodeHaveChildPackList.contains(operateCode)) {
						errorMsg = new StringBuilder("当前码非指定").append(bigUnit).append("的")
								.append(packUnit).append("码");
						qRCodeError = new QRCodeError(operateCode, codeLevel, codeProductId, errorMsg.toString());
					}
				}
			}
			if(qRCodeError!=null) {
				qRCodeErrorList.add(qRCodeError); // 加入错误列表
				qRCodeError = null; // 下次循环先置为null
			}
		}
	}

	/**
	 *@Title: validateIsExist
	 *@Description: 验证工单号是否存在
	 *@return boolean
	 *@author youjun
	 *@date 2017年8月19日 下午8:56:16
	 *@param workOrder
	 *@param enterpriseCode
	 *@return
	 * @throws ServiceException 
	 */
	private boolean validateIsExist(final String workOrder, final String enterpriseCode) throws ServiceException {
		
		if(StringUtils.isNotBlank(workOrder)) {
			final DismantlePileStack dismantlePileStackCondition = new DismantlePileStack();
			dismantlePileStackCondition.setWorkOrder(workOrder);
			try {
				final List<DismantlePileStack> list = dismantlePileStackDao.findListByCondition(dismantlePileStackCondition, enterpriseCode);
				if(!CollectionUtils.isEmpty(list)) {
					return true; // 代表已存在
				}
			} catch (DaoException e) {
				throw new ServiceException(e);
			}
		}
		return false;
	}

	/**
	 *@Title: validateUploadParams
	 *@Description: TODO()
	 *@return Response
	 *@author youjun
	 *@date 2017年8月19日 下午8:55:56
	 *@param packBoxStackVo
	 *@return
	 */
	private Response validateUploadParams(PackBoxStackVo packBoxStackVo) {
		final int operationType = packBoxStackVo.getOperationType();
		if(operationType!=PackScanOperTypeEnum.GROUP_OR_FILL_STACK.getId()
				&&operationType!=PackScanOperTypeEnum.SPLIT_STACK.getId()
				&&operationType!=PackScanOperTypeEnum.ADD_NEW_STACK.getId()) {
			return new Response(Cons.RESPONSE_CODE_PARAM_INVALID, "对最高规格单位不支持此操作");
		}
		return new Response(ResponseEnum.SUCCESS);
	}
	
}
